package com.njmaomao.gj.pyot.service;

import com.njmaomao.gj.pyot.common.ResponseCode;
import com.njmaomao.gj.pyot.common.XmlUtil;
import com.njmaomao.gj.pyot.common.exception.SystemBizFail;
import com.njmaomao.gj.pyot.config.Bizs;
import com.njmaomao.gj.pyot.domain.wechat.PayConfig;
import com.njmaomao.gj.pyot.domain.wechat.PayConfigFactory;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.PayNotification;
import com.njmaomao.gj.pyot.domain.wechat.wechatapi.RefundNotification;
import com.njmaomao.gj.pyot.domain.model.PayRecord;
import com.njmaomao.gj.pyot.domain.model.RefundRecord;
import com.njmaomao.gj.pyot.repository.PayRecordRepository;
import com.njmaomao.gj.pyot.repository.RefundRecordRepository;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.Map;


/**
 * @author dylan
 * @time 2018/5/21.
 */
@Service
public class WechatNotificationAppService {

    private Logger log = LoggerFactory.getLogger(WechatNotificationAppService.class);

    @Autowired
    private PayRecordRepository payRecordRepository;
    @Autowired
    private RefundRecordRepository refundRecordRepository;
    @Autowired
    private PayConfigFactory payConfigFactory;
    @Autowired
    private CallbackSevice callbackService;


    /**
     * 处理支付结果通知
     *
     * @param payNotification 微信传过来的支付结果通知
     */
    @Transactional
    public void handlePayNotification(PayNotification payNotification) {

        if (payNotification.success()) {
            paySuccess(
                    payNotification.getOutTradeNo(),
                    payNotification.getTotalFee(),
                    payNotification.getOpenid(),
                    payNotification.getTimeEndForDate(),
                    payNotification.getTransactionId()
            );
        }

    }

    public void paySuccess(String orderCode, int totalFeeWc, String openId, Date timeEndForDate, String transactionId){
        PayRecord payRecord = updatePayRecordPaid(orderCode, totalFeeWc, openId, timeEndForDate, transactionId);
        callbackService.payResultCallback(payRecord);
    }


    /**
     *检查sign是否正确
     * @param xmlBody
     * @param appId
     * @return
     */
    public boolean checkSign(String xmlBody, String appId, String mchId){
        Map<String,String> bodyMap = XmlUtil.xmltoMap(xmlBody);
        String insign = bodyMap.get("sign");
        PayConfig payConfig = payConfigFactory.get(appId, mchId);
        String sign = Bizs.getMd5Sign(bodyMap,payConfig.getKey());
        boolean same = StringUtils.equals(insign,sign);
        if(!same){
            log.error("EVENT=支付&退款结果通知|MESSAGE=签名不正确|SOURCE_SIGN={},SIGN={}",insign,sign);
        }
        return same;
    }


    public PayRecord updatePayRecordPaid(String orderCode, int totalFeeWc, String openid, Date timeEnd, String transactionId) {

        PayRecord payRecord = payRecordRepository.findByOrderCode(orderCode);
        SystemBizFail.whenIf(payRecord == null, ResponseCode.PAYTO.UNKNOW_ORDER);

        if ((payRecord.getTotalAmount() != totalFeeWc)) {
            log.error("支付金额跟支付记录的金额不一致|ORDER_CODE={}|PAY_AMOUNT={}|TOTAL_FEE={}", payRecord.getOrderCode(), payRecord.getTotalAmount(), totalFeeWc);
            SystemBizFail.happen(ResponseCode.PAYTO.TOTALFEE_DISCORD);
        }

        if (payRecord.paid()) {
            log.info("订单已更新为支付成功|ORDER_CODE={}", payRecord.getOrderCode());
            return payRecord;
        }

        payRecord.setUserId(openid);
        payRecord.setPayTime(timeEnd);
        payRecord.setTradeNo(transactionId);
        payRecord.setStatus(PayRecord.Status.PAYED.getStatus());

        payRecordRepository.save(payRecord);
        return payRecord;
    }

    /**
     * 处理退款结果通知
     *
     * @param xmlBody 微信传过来的退款结果通知消息体
     */
    @Transactional
    public void handleRefundNotification(String xmlBody, String appid, String mchId, String encryptData) {

        RefundNotification refundNotification = null;

        //TODO 这里会有问题，须保证商城ID和Appid是一对一
        PayConfig payConfig = payConfigFactory.get(appid, mchId);
        try {
            String decryptStr = Bizs.decryptWechatRefundNotify(encryptData, payConfig.getKey());

            String replace = decryptStr.replace("<root>", "").replace("</root>", "");

            String decryptXml = xmlBody.replace("</xml>", replace + "</xml>");

            refundNotification = XmlUtil.xml2Bean(decryptXml, RefundNotification.class);
        } catch (Exception e) {
            log.error("decrypt WechatRefundNotify error", e);
            SystemBizFail.happen(ResponseCode.PAYTO.DECRYPTWECHATREFUNDNOTIFY_ERROR, e.getMessage());
        }

        String refundCode = refundNotification.getOutRefundNo();
        RefundRecord refundRecord = refundRecordRepository.findByRefundNo(refundCode);

        if (refundRecord.getStatus() == RefundRecord.Status.REFUNDED.getStatus()) {
            log.warn("EVENT=退款结果通知|MESSAGE=该订单已退款完成|REFUND_CODE={}", refundCode);
            return;
        }

        if(refundRecord.getRefundAmount() != refundNotification.getRefundFee()){
            log.error("EVENT=退款结果通知|MESSAGE=退款金额不一致|REFUND_CODE={}", refundCode);
            SystemBizFail.happen(ResponseCode.PAYTO.REFUND_AMOUNT_ERROR);
        }
        refundRecord.setRefundId(refundNotification.getRefundId());
        refundRecord.setRefundTime(refundNotification.getSuccessTimeForDate());
        refundRecord.setMsg(refundNotification.getReturnMsg());

        if (refundNotification.success()) {
            log.info("EVENT=退款结果通知|MESSAGE=更新退款订单为已退款|REFUND_CODE={}",refundCode);
            refundRecord.setStatus(RefundRecord.Status.REFUNDED.getStatus());
        } else {
            log.info("EVENT=退款结果通知|MESSAGE=更新退款订单为退款失败|REFUND_CODE={}",refundCode);
            refundRecord.setStatus(RefundRecord.Status.REFUNDFAIL.getStatus());
        }
        refundRecordRepository.save(refundRecord);

        callbackService.refundResultCallback(refundRecord);

    }


}
